/*
 +-----------------------------------------------------------------------------------------------+
 | Project : UKAPI
 +-----------------------------------------------------------------------------------------------+
 | Project Description : Universal & Kernel API
 +-----------------------------------------------------------------------------------------------+
 | File Description :
 |   
 |                                                 
 +-----------------------------------------------------------------------------------------------+
 | Licence : MIT Licence
 +-----------------------------------------------------------------------------------------------+
 | Copyright (c) 2007 Olivier Vanhoucke
 | 
 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software 
 | and associated documentation files (the "Software"), to deal in the Software without 
 | restriction, including without limitation the rights to use, copy, modify, merge, publish, 
 | distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the 
 | Software is furnished to do so, subject to the following conditions:
 | 
 | The above copyright notice and this permission notice shall be included in all copies or 
 | substantial portions of the Software.
 | 
 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
 | BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 +-----------------------------------------------------------------------------------------------+
*/

#include <ukapi.h>

//
// Functional Prototypes
//
static void output_byte( char_ascii_t c );
static void output_number_decimal( ulong_t n );
static void output_number_hexa( ulong_t n, long_t depth );
static void output_string( const string_ascii_t s );

char * szSprintf; // put in parameters ?????????????????????????

/*****************************************************************************
*
*
*   @func   void    |   FormatString | Simple formatted output string routine
*
*   @rdesc  Returns length of formatted string
*
*   @parm   unsigned char * |   pBuf |
*               Pointer to string to return formatted output.  User must ensure
*               that buffer is large enough.
*
*   @parm   const unsigned char * |   sz,... |
*               Format String:
*
*               @flag Format string | type
*               @flag u | unsigned
*               @flag d | int
*               @flag c | char
*               @flag s | string
*               @flag x | 4-bit hex number
*               @flag B | 8-bit hex number
*               @flag H | 16-bit hex number
*               @flag X | 32-bit hex number
*
*   @comm
*           Same as OutputFormatString, but output to buffer instead of serial port.
*/
ulong_t string_ascii_format( string_ascii_t pBuf, const string_ascii_t sz, ... )
{
	ulong_t l = 0;
	
    va_list_t vl;
    
    varg_start( vl, sz );
	
	l = string_ascii_argsformat( pBuf, sz, vl );
	
    varg_end( vl );	
	
	return l;
}

ulong_t string_ascii_argsformat( string_ascii_t pBuf, const string_ascii_t sz, va_list_t vl )
{
    char_ascii_t    c;
	string_ascii_t ssz = (string_ascii_t) sz;

    szSprintf = pBuf;
	
    while ( *ssz ) 
	{
        c = *ssz++;
		
        switch ( c ) 
		{
		case '%':
            c = *ssz++;
            switch (c) 
			{ 
            case 'x':
                output_number_hexa( varg_arg( vl, ulong_t ), 0 );
                break;
            case 'B':
                output_number_hexa( varg_arg( vl, ulong_t ), 2 );
                break;
            case 'H':
                output_number_hexa( varg_arg( vl, ulong_t ), 4 );
                break;
            case 'X':
                output_number_hexa( varg_arg( vl, ulong_t ), 8 );
                break;
            case 'd':
                {
                    long_t l;
                
                    l = varg_arg( vl, long_t);
					
                    if (l < 0) 
					{ 
                        output_byte( '-' );
                        l = - l;
                    }
                    output_number_decimal( (ulong_t) l );
                }
                break;
            case 'u':
                output_number_decimal( varg_arg( vl, ulong_t ) );
                break;
            case 's':
                output_string( varg_arg( vl, string_ascii_t ) );
                break;
            case '%':
                output_byte( '%' );
                break;
            case 'c':
                c = varg_arg( vl, char_ascii_t );
                output_byte( c );
                break;
                
            default:
                output_byte( ' ' );
                break;
            }
            break;
        case '\r':
            if ( *ssz == '\n' )
			{
                ssz++;
			}
            c = '\n';
            // fall through
        case '\n':
            output_byte( '\r' );
            // fall through
        default:
            output_byte(c);
        }
    }
	
    output_byte( 0 );
    c = szSprintf - pBuf;
    szSprintf = 0;
	
    return c;
}

/*****************************************************************************
*
*
*   @func   void    |   pOutputByte | Sends a byte out of the monitor port.
*
*   @rdesc  none
*
*   @parm   unsigned int |   c |
*               Byte to send.
*
*/
static void output_byte( char_ascii_t c ) // rename as internal
{
    if ( szSprintf != null )
	{
        *szSprintf++ = c;
	}
}


/*****************************************************************************
*
*
*   @func   void    |   pOutputNumHex | Print the hex representation of a number through the monitor port.
*
*   @rdesc  none
*
*   @parm   unsigned long |   n |
*               The number to print.
*
*   @parm   long | depth |
*               Minimum number of digits to print.
*
*/
static void output_number_hexa( ulong_t n, long_t depth ) // rename as internal
{
    if ( depth != 0 ) 
	{
        depth--;
    }
    
    if ( 	( n & ~0x0f ) 
		|| 	( depth != 0 ) 
		)		
	{
        output_number_hexa( n >> 4, depth );
        n &= 0xf;
    }
    
    if ( n < 10 ) 
	{
        output_byte( (char_ascii_t) ( n + '0' ) );
    } 
	else 
	{ 
		output_byte( (char_ascii_t) ( n - 10 + 'A' ) );
	}
}


/*****************************************************************************
*
*
*   @func   void    |   pOutputNumDecimal | Print the decimal representation of a number through the monitor port.
*
*   @rdesc  none
*
*   @parm   unsigned long |   n |
*               The number to print.
*
*/
static void output_number_decimal( ulong_t n ) // rename as internal
{
    if ( n >= 10 ) 
	{
        output_number_decimal( n / 10 );
        n %= 10;
    }
	
    output_byte( (char_ascii_t) ( n + '0' ) );
}


/*****************************************************************************
*
*
*   @func   void    |   OutputString | Sends an unformatted string to the monitor port.
*
*   @rdesc  none
*
*   @parm   const unsigned char * |   s |
*               points to the string to be printed.
*
*   @comm
*           backslash n is converted to backslash r backslash n
*/
static void output_string( const string_ascii_t s ) // rename as internal
{
	string_ascii_t ss = (string_ascii_t) s;

	if ( s == null )
	{
		return;
	}
	
    while ( *ss != '\0' )
	{        
        if ( *ss == '\n' ) 
		{
            output_byte( '\r' );
        }
		
        output_byte( *ss++ );
    }
}